/* original code copyright 2004 Christopher W. Cowell-Shah http://www.cowell-shah.com/research/benchmark/code */
/* other code portions copyright http://dada.perl.it/shootout/ and Doug Bagley http://www.bagley.org/~doug/shootout */
/* combined, modified and fixed by Thomas Bruckschlegel - http://www.tommti-systems.com */

//#include "stdafx.h"
#include <time.h>
//#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>


int Hi = 0, Lo = 0;

double intArithmetic(int);
double doubleArithmetic(double, double);
double longArithmetic(long long, long long);
double trig(double);
double io(int);

double array(int n);
double except(int volatile N);
double hashtest(int n);
double hashes(int n);
double hs(int N);
double vs(int n);
double mm(int n);
double nl(int n);
double sc(int n);

int main()
{
    int intMax =		1000000000; // 1B
    double doubleMin =  10000000000.0; // 10B
    double doubleMax =	11000000000.0; // 11B
    long long longMin = 10000000000; // 10B
    long long longMax = 11000000000; // 11B
    double trigMax =	10000000; // 10M
    int ioMax =			1000000; // 1M
    
    printf("Start C++ benchmark\n");
    
    long intArithmeticTime = (long)intArithmetic(intMax);
    long doubleArithmeticTime = (long)doubleArithmetic(doubleMin, doubleMax);
    long longArithmeticTime = (long)longArithmetic(longMin, longMax);
    long trigTime = (long)trig(trigMax);
    long ioTime = (long)io(ioMax);
    
    int n=10;
    long arrayTime = (long)array(n*10000);
    long ExceptionTime = (long)except(n*100000);
    long hashtestTime = (long)hashtest(n*10000);
    long hashesTime = (long)hashes(n*100);
    long hsTime = (long)hs(n*100000);
    long vsTime = (long)vs(n*10);
    long mmTime = (long)mm(n*10000);
    long nlTime = (long)nl(n*4);
    long scTime = (long)sc(n*1000000);
    
    long totalTime =intArithmeticTime + doubleArithmeticTime + longArithmeticTime + trigTime + ioTime
    +arrayTime
    +ExceptionTime
    +hashtestTime
    +hashesTime
    +hsTime
    +vsTime
    +mmTime
    +nlTime
    +scTime;
    
    
    printf("Total elapsed time: %d ms\n", totalTime);
    
    printf("Stop C++ benchmark\n");
    //getch();
    return 0;
}

//////////////
double intArithmetic(int intMax)
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    int intResult = 1;
    int i = 1;
    while (i < intMax)
    {
        intResult -= i++;
        intResult += i++;
        intResult *= i++;
        intResult /= i++;
    }
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("Int arithmetic elapsed time: %1.0f ms with intMax of %ld\n", elapsedTime, intMax);
    printf(" i: %d\n", i);
    printf(" intResult: %d\n", intResult);
    return elapsedTime;
}


//////////////
double doubleArithmetic(double doubleMin, double doubleMax)
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    double doubleResult = doubleMin;
    double i = doubleMin;
    while (i < doubleMax)
    {
        doubleResult -= i++;
        doubleResult += i++;
        doubleResult *= i++;
        doubleResult /= i++;
    }
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("Double arithmetic elapsed time: %1.0f ms with doubleMin %.15f, doubleMax %.15f\n", elapsedTime, doubleMin, doubleMax);
    printf(" i: %f\n", i);
    printf(" doubleResult: %.15f\n", doubleResult);
    return elapsedTime;
}



//////////////
double longArithmetic(long long longMin, long long longMax)
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    long long longResult = longMin;
    long long i = longMin;
    while (i < longMax)
    {
        longResult -= i++;
        longResult += i++;
        longResult *= i++;
        longResult /= i++;
    }
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("Long arithmetic elapsed time: %1.0f ms with longMax %I64d\n", elapsedTime, longMax);
    printf(" i: %I64d\n", i);
    printf(" longResult: %I64d\n", longResult);
    return elapsedTime;
}


//////////////
double trig(double trigMax)
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    double sine;
    double cosine;
    double tangent;
    double logarithm;
    double squareRoot;
    
    double i = 0.0;
    while (i < trigMax)
    {
        sine = sin(i);
        cosine = cos(i);
        tangent = tan(i);
        logarithm = log10(i);
        squareRoot = sqrt(i);
        i++;
    }
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("Trig elapsed time: %1.0f ms with max of %1.0f\n", elapsedTime, trigMax);
    printf(" i: %f\n", i);
    printf(" sine: %.15f\n", sine);
    printf(" cosine: %.15f\n", cosine);
    printf(" tangent: %.15f\n", tangent);
    printf(" logarithm: %.15f\n", logarithm);
    printf(" squareRoot: %.15f\n", squareRoot);
    return elapsedTime;
}


//////////////
double io(int ioMax)
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    FILE *stream;
    stream = fopen("C:\\TestC.txt", "w");
    int i = 0;
    while (i++ < ioMax)
    {
        fputs("abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefgh\n", stream);
    }
    fclose(stream);
    
    char readLine[100];
    stream = fopen("C:\\TestC.txt", "r");
    i = 0;
    while (i++ < ioMax)
    {
        fgets(readLine, 100, stream);
    }
    fclose(stream);
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("I/O elapsed time: %1.0f ms with max of %ld\n", elapsedTime, ioMax);
    printf(" i: %d\n", i);
    printf(" readLine: %s\n", readLine);
    
    return elapsedTime;
}
///// update
//////////////
double array(int n)
{
    int i, k, *x, *y;
    
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    x = new int[n];
    y = new int[n];
    
    for (i = 0; i < n; i++) {
        x[i] = i + 1;
        y[i] = 0;
    }
    for (k=0; k<1000; k++) {
        for (i = n-1; i >= 0; i--) {
            y[i] += x[i];
        }
    }
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("array elapsed time: %1.0f ms - %ld %ld\n", elapsedTime, y[0], y[n-1]);
    
    delete[] x;
    delete[] y;
    
    return elapsedTime;
}

static int count = 0;
class HiException
{
};

class LoException
{
};


void BlowUp(int n)
{
    if ((n & 1) == 0)
    {
        throw LoException();
    }
    else
    {
        throw HiException();
    }
}

void LoFunction(int n)
{
    try
    {
        BlowUp(n);
    }
    catch (LoException)
    {
        Lo++;
    }
}

void HiFunction(int n)
{
    try
    {
        LoFunction(n);
    }
    catch (HiException)
    {
        Hi++;
    }
}

void SomeFunction(int n)
{
    HiFunction(n);
}


double except(int N)
{
    
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    while (N) {
        SomeFunction(N);
        N--;
    }
    //printf("Exceptions: HI=%d / LO=%d\n", HI, LO);
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("Exceptions elapsed time: %1.0f ms - HI=%d / LO=%d\n", elapsedTime, Hi, Lo);
    
    return elapsedTime;
}



#include <map>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
using namespace std;

//////////////
double hashtest(int n)
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    map <string, int > X;
    int c = 0;
    
    if(n < 1) n = 1;
    
    for(int i=1; i<=n; i++) {
        std::ostringstream oss;
        oss << i;
        X.insert( map <string, int> :: value_type ( oss.str() , i ) );
    }
    
    for(int i=n; i>0; i--) {
        std::ostringstream oss;
        oss << i;
        if(X[oss.str()]!=0) c++;
    }
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("HashMap elapsed time: %1.0f ms - %d\n", elapsedTime, c);
    
    return elapsedTime;
}

//////////////
double hashes(int n)
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    map <string, int > hash1;
    map <string, int > hash2;
    int c = 0;
    
    for(int i=0; i<10000; i++)
    {
        std::ostringstream oss;
        oss << i;
        hash1.insert( map <string, int> :: value_type ( string("foo_")+oss.str() , i ) );
    }
    
    map <string, int> :: iterator h1_it;
    map <string, int> :: iterator h2_it;
    for(int i=0; i<n; i++)
    {
        for ( h1_it = hash1.begin( ) ; h1_it != hash1.end( ); h1_it++)
        {
            h2_it=hash2.find( h1_it->first );
            if(hash2.end()!=h2_it)
            {
                int v1 = (int) h1_it->second;
                int v2 = (int) h2_it->second;
                h2_it->second =  v1 + v2;
            }
            else
            {
                hash2.insert( map <string, int> :: value_type ( h1_it->first , h1_it->second ) );
            }
        }
    }
    //	printf("%d\" \"%d\" \"%d\" \"%d\n", hash1["foo_1"], hash1["foo_9999"], hash2["foo_1"], hash2["foo_9999"]);
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("HashMaps elapsed time: %1.0f ms - %d\" \"%d\" \"%d\" \"%d\n", elapsedTime, hash1["foo_1"], hash1["foo_9999"], hash2["foo_1"], hash2["foo_9999"]);
    return elapsedTime;
}

#define IM 139968
#define IA   3877
#define IC  29573

double gen_random(double max) {
    static long last = 42;
    return( max * (last = (last * IA + IC) % IM) / IM );
}

void heapsort(int n, double *ra) {
    int i, j;
    int ir = n;
    int l = (n >> 1) + 1;
    double rra;
    
    for (;;) {
        if (l > 1) {
            rra = ra[--l];
        } else {
            rra = ra[ir];
            ra[ir] = ra[1];
            if (--ir == 1) {
                ra[1] = rra;
                return;
            }
        }
        i = l;
        j = l << 1;
        while (j <= ir) {
            if (j < ir && ra[j] < ra[j+1]) { ++j; }
            if (rra < ra[j]) {
                ra[i] = ra[j];
                j += (i = j);
            } else {
                j = ir + 1;
            }
        }
        ra[i] = rra;
    }
}

//////////////
double hs(int N)
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    double *ary;
    int i;
    
    ary = new double[N+1];
    for (i=1; i<=N; i++) {
        ary[i] = gen_random(1);
    }
    
    heapsort(N, ary);
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("Heapsort elapsed time: %1.0f ms - %.10g\n", elapsedTime, ary[N]);
    
    delete[] ary;
    return elapsedTime;
}


#define lSIZE 10000

int VectorTest()
{
    // create a list of integers (Li1) from 1 to SIZE
    vector<int> Li1;
    for (int i = 1; i < lSIZE + 1; i++)
    {
        
        Li1.push_back((int)i);	//addlast
    }
    // copy the list to Li2 (not by individual items)
    vector<int> Li2=Li1;
    vector<int> Li3;
    // remove each individual item from left side of Li2 and
    // append to right side of Li3 (preserving order)
    while (!Li2.empty())
    {
        Li3.push_back((int)*Li2.begin() ); //addlast
        Li2.erase(Li2.begin());
    }
    // Li2 must now be empty
    // remove each individual item from right side of Li3 and
    // append to right side of Li2 (reversing list)
    while (!Li3.empty())
    {
        Li2.push_back( (int)*(Li3.end()-1) ); //addlast
        Li3.erase(Li3.end()-1);
    }
    // Li3 must now be empty
    // reverse Li1
    vector<int> tmp;
    while (!Li1.empty())
    {
        tmp.insert(tmp.begin() , (int)Li1[0]); //addfirst
        Li1.erase(Li1.begin());
    }
    Li1 = tmp;
    // check that first item is now lSIZE
    if ( (int)(Li1[0]) != lSIZE )
    {
        printf("first item of Li1 != lSIZE");
        return (0);
    }
    
    // compare Li1 and Li2 for equality
    if(Li1!=Li2)
    {
        printf("Li1 and Li2 differ");
        return (0);
    }
    // return the length of the list
    return (Li1.size());
}
//////////////
double vs(int n)
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    int result = 0;
    while(n--) result = VectorTest();
    //    printf("%d\n", result);
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("Vector elapsed time: %1.0f ms - %d\n", elapsedTime, result);
    return elapsedTime;
}

#define mSIZE 30

int **mkmatrix(int rows, int cols) {
    int i, j, count = 1;
    int **m = (int **) malloc(rows * sizeof(int *));
    for (i=0; i<rows; i++) {
        m[i] = (int *) malloc(cols * sizeof(int));
        for (j=0; j<cols; j++) {
            m[i][j] = count++;
        }
    }
    return(m);
}

void zeromatrix(int rows, int cols, int **m) {
    int i, j;
    for (i=0; i<rows; i++)
        for (j=0; j<cols; j++)
            m[i][j] = 0;
}

void freematrix(int rows, int **m) {
    while (--rows > -1) { free(m[rows]); }
    free(m);
}

int **mmult(int rows, int cols, int **m1, int **m2, int **m3) {
    int i, j, k, val;
    for (i=0; i<rows; i++) {
        for (j=0; j<cols; j++) {
            val = 0;
            for (k=0; k<cols; k++) {
                val += m1[i][k] * m2[k][j];
            }
            m3[i][j] = val;
        }
    }
    return(m3);
}

//////////////
double mm(int n){
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    
    int i;
    
    int **m1 = mkmatrix(mSIZE, mSIZE);
    int **m2 = mkmatrix(mSIZE, mSIZE);
    int **mm = mkmatrix(mSIZE, mSIZE);
    
    for (i=0; i<n; i++) {
        mm = mmult(mSIZE, mSIZE, m1, m2, mm);
    }
    //printf("%d %d %d %d\n", mm[0][0], mm[2][3], mm[3][2], mm[4][4]);
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("Matrix Multiply elapsed time: %1.0f ms - %d %d %d %d\n", elapsedTime, mm[0][0], mm[2][3], mm[3][2], mm[4][4]);
    
    freematrix(mSIZE, m1);
    freematrix(mSIZE, m2);
    freematrix(mSIZE, mm);
    return elapsedTime;
};

//////////////
double nl(int n) {
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();
    int a, b, c, d, e, f;
    int x=0;
    
    for (a=0; a<n; a++)
        for (b=0; b<n; b++)
            for (c=0; c<n; c++)
                for (d=0; d<n; d++)
                    for (e=0; e<n; e++)
                        for (f=0; f<n; f++)
                            x+=a+b+c+d+e+f;
    
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("Nested Loop elapsed time: %1.0f ms %d\n", elapsedTime, x);
    return elapsedTime;
}


//////////////
double sc(int n) {
    double elapsedTime;
    wchar_t *test=L"hello";
    clock_t stopTime;
    clock_t startTime = clock();
    
    int i, buflen = 8;
    wchar_t *strbuf = new wchar_t[n*8];
    strbuf[0]='\0';
    
    int i3=0;
    for (i=0; i<n; i++)
    {
        for (int i2=0; i2<wcslen(test); i2++)
        {
            strbuf[i3]=test[i2];
            i3++;
        }
    }
    stopTime = clock();
    elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
    printf("String Concat. (fixed) elapsed time: %1.0f ms\n", elapsedTime);//, strbuf);
    delete strbuf;
    
    return elapsedTime;
}
